using System;
using System.IO;
using System.Web;
using System.Linq;
using System.Web.Mvc;
using System.Globalization;
using Stimulus360.Services;
using Stimulus360.Models.Cms;
using Stimulus360.Models.Data;
using System.Web.Configuration;
using Stimulus360.Services.Interfaces;
using Stimulus360.Models.Data.CustomTypes;

namespace Stimulus360.Controllers.Projects
{
    [HandleError]
    public class ProjectsController : Controller
    {
        /// <summary>
        ///     Instance of ProjectService to be used
        /// </summary>
        private readonly IProjectService _projectService;

        /// <summary>
        ///     Instance of InvestmentCategoryService to be used
        /// </summary>
        private readonly IInvestmentCategoryService _investmentCategoryService;

        /// <summary>
        ///     The number projects being shown in the screen
        /// </summary>
        private readonly string _pageSize = WebConfigurationManager.AppSettings["PageSize"];

        /// <summary>
        ///     Instance of UserProfileService to be used
        /// </summary>
        private readonly IUserService _userProfileService;

        /// <summary>
        ///     Default Constructor. Instantiates the services
        /// </summary>
        public ProjectsController()
            : this(new UserService(), new ProjectService(), new InvestmentCategoryService())
        {
        }

        /// <summary>
        ///     Overloaded constructor to allow Dependency Injection.
        /// </summary>
        /// <param name="userService">
        ///     The User service instance to be used.
        /// </param>
        /// <param name="projectService">
        ///     The Project service instance to be used.
        /// </param>
        /// <param name="investmentService">
        ///     The InvestmentCategoryService to be used.
        /// </param>
        public ProjectsController(IUserService userService, IProjectService projectService, IInvestmentCategoryService investmentService)
        {
            _userProfileService = userService ?? new UserService();
            _projectService = projectService ?? new ProjectService();
            _investmentCategoryService = investmentService ?? new InvestmentCategoryService();
        }

        /// <summary>
        ///     Method to return a list of New Projects
        /// </summary>
        /// <param name="page">
        ///     Current Page No
        /// </param>
        /// <returns>
        ///     ViewModel containing list of new Projects.
        /// </returns>
        public ActionResult Index(int? page)
        {
            NewProjectsListingViewModel model = new NewProjectsListingViewModel
            {
                Projects = _projectService.GetNew(page.GetValueOrDefault(0).ToString(CultureInfo.CurrentCulture), _pageSize),
                Categories = _investmentCategoryService.GetAll()
            };

            return View(model);
        }

        /// <summary>
        ///     Method return list of Projects in the given category
        /// </summary>
        /// <param name="categoryTitle">
        ///     Category for which list of project is to be retrieved.
        /// </param>
        /// <param name="page">
        ///     Page No.of screen to be shown
        /// </param>
        /// <returns>
        ///     Returns ViewModel containing Projects for the given category
        /// </returns>
        public ActionResult Category(string categoryTitle, int? page)
        {
            CategorizedProjectsListingViewModel model = new CategorizedProjectsListingViewModel
            {
                Categories = _investmentCategoryService.GetAll(),
                Projects = _projectService.FindInCategory(categoryTitle, page.GetValueOrDefault(0).ToString(CultureInfo.CurrentCulture), _pageSize),
                Category = categoryTitle
            };
            return View(model);
        }

        /// <summary>
        ///     Method searches for Projects based on the given criteria
        /// </summary>
        /// <param name="criteria">
        ///     Criteria can by top N, status or Volunteer
        /// </param>
        /// <param name="query">
        ///     Criteria string to be processed
        /// </param>
        /// <param name="page">
        ///      Page No. of the project search page
        /// </param>
        /// <returns>
        ///     Returns search view
        /// </returns>
        public ActionResult Search(string criteria, string query, int? page)
        {
            ProjectsSearchViewModel model = new ProjectsSearchViewModel
            {
                Projects = _projectService.Search(criteria, query, page.GetValueOrDefault(0).ToString(CultureInfo.CurrentCulture), _pageSize),
                Categories = _investmentCategoryService.GetAll(),
                SearchCriteria = criteria,
                SearchString = query
            };
            return View(model);
        }

        /// <summary>
        ///     Method to return details of the Project
        /// </summary>
        /// <param name="projectId">
        ///     ProjectId for which details are required
        /// </param>
        /// <returns>
        ///     ViewModel containing Project Details
        /// </returns>
        public ActionResult Details(int projectId)
        {
            ProjectDetailViewModel viewModel = new ProjectDetailViewModel
                                                   {
                                                       Project = _projectService.FindById(projectId.ToString(CultureInfo.CurrentCulture)),
                                                       RelatedProjects = _projectService.GetRelated(projectId.ToString(CultureInfo.CurrentCulture))
                                                   };

            return View(viewModel);

        }


        /// <summary>
        ///     Method to return the list of feedback submitted for the given Project
        /// </summary>
        /// <param name="projectId">
        ///     ProjectId for which feedback is to  be retrieved.
        /// </param>
        /// <param name="page">
        ///     Current Page No. 
        /// </param>
        /// <returns>
        ///     ViewModel containing list of feedback for the given project.
        /// </returns>
        public ActionResult Feedback(int projectId, int? page)
        {
            ProjectFeedbackListingViewModel model = new ProjectFeedbackListingViewModel
            {
                ProjectSummary = GetProjectSummary(projectId),
                Feedback = _projectService.GetFeedback(projectId.ToString(CultureInfo.CurrentCulture), page.GetValueOrDefault(0).ToString(CultureInfo.CurrentCulture), _pageSize)
            };

            return View(model);
        }

        /// <summary>
        ///     GET method to add a new feedback
        /// </summary>
        /// <param name="projectId">
        ///     IdeaId for which feedback will be added
        /// </param>
        /// <returns>
        ///     GET ViewModel for Add Feedback
        /// </returns>
        [Authorize]
        public ActionResult SubmitFeedback(int projectId)
        {
            ProjectRating projectRating;

            bool alreadyPresent = false;
            ProjectRating originalRating = _projectService.GetFeedback(User.Identity.Name, projectId.ToString(CultureInfo.CurrentCulture));
            if (originalRating == null)
            {
                projectRating = new ProjectRating { ProjectId = projectId };
            }
            else
            {
                projectRating = originalRating;
                alreadyPresent = true;
            }
            ProjectFeedbackSubmissionViewModel model = new ProjectFeedbackSubmissionViewModel
            {
                ProjectSummary = GetProjectSummary(projectId),
                AlreadySubmitted = alreadyPresent,
                Feedback = projectRating
            };
            return View(model);
        }

        /// <summary>
        ///     POST Method to Add feedback
        /// </summary>
        /// <param name="feedback">
        ///     Feedback object to be added
        /// </param>
        /// <returns>
        ///     Model with new Feedback
        /// </returns>
        [ValidateAntiForgeryToken]
        [Authorize]
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult SubmitFeedback(ProjectRating feedback)
        {
            if (!feedback.IsValid)
            {
                ModelState.AddModelError(feedback.GetRuleViolations());
            }
            else
            {

                feedback.UserName = User.Identity.Name;
                feedback.DateSubmitted = DateTime.Now;
                _projectService.AddFeedback(feedback);
                return RedirectToAction("Feedback", "Projects", new { projectId = feedback.ProjectId });
            }

            return View(new ProjectFeedbackSubmissionViewModel
            {
                ProjectSummary = GetProjectSummary(feedback.ProjectId),
                Feedback = feedback
            });
        }

        /// <summary>
        ///     GET method to add a new document
        /// </summary>
        /// <param name="projectId">
        ///     ProjectId for which documents is to  be retrieved.
        /// </param>
        /// <returns>
        ///     GET ViewModel for add document
        /// </returns>
        [Authorize]
        public ActionResult SubmitDocument(int projectId)
        {
            SubmitDocumentViewModel viewModel = new SubmitDocumentViewModel
                                                    {
                                                        ProjectSummary = GetProjectSummary(projectId),
                                                        Document = new ProjectDocument { ProjectId = projectId }
                                                    };

            return View(viewModel);
        }

        /// <summary>
        ///     POST Method to Add document
        /// </summary>
        /// <param name="document">
        ///     Document object to be added
        /// </param>
        /// <returns>
        ///     Model with new document
        /// </returns>
        [Authorize]
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult SubmitDocument(ProjectDocument document)
        {
            HttpPostedFileBase uploadedfile = Request.Files[0];
            string fileName = uploadedfile.FileName;
            document.FilePath = fileName;
            string type = Path.GetExtension(fileName).Replace(".", string.Empty).ToLower(CultureInfo.CurrentCulture);
            try
            {
                DocType docType = (DocType)Enum.Parse(typeof(DocType), type.ToSentenceCasing());
                document.DocumentType = (short)docType;
            }
            catch (ArgumentException)
            {
                document.DocumentType = (short)DocType.None;
            }
            if (!document.IsValid)
            {
                ModelState.AddModelError(document.GetRuleViolations());
            }
            else
            {
                document.SubmittedBy = User.Identity.Name;
                document.DateUploaded = DateTime.Now;

                byte[] documentData = new byte[uploadedfile.InputStream.Length];
                uploadedfile.InputStream.Read(documentData, 0, documentData.Length);
                document.Contents = new System.Data.Linq.Binary(documentData);

                _projectService.AddDocument(document);
                return RedirectToAction("Documents", "Projects", new { projectId = document.ProjectId });
            }
            SubmitDocumentViewModel viewModel = new SubmitDocumentViewModel
                                                    {
                                                        ProjectSummary = GetProjectSummary(document.ProjectId),
                                                        Document = document
                                                    };
            return View(viewModel);
        }

        /// <summary>
        ///     Method to return the list of Photos submitted for the given Project
        /// </summary>
        /// <param name="projectId">
        ///     ProjectId for which photos is to be retrieved.
        /// </param>
        /// <param name="page">
        ///     Current Page No.
        /// </param>
        /// <returns>
        ///     ViewModel containing list of photos for the given project.
        /// </returns>
        public ActionResult Photos(int projectId, int? page)
        {
            ProjectPhotosViewModel viewModel = new ProjectPhotosViewModel
                                                   {
                                                       Photos = _projectService.GetPhotos(projectId.ToString(CultureInfo.CurrentCulture), page.GetValueOrDefault(0).ToString(CultureInfo.CurrentCulture), _pageSize),
                                                       ProjectSummary = GetProjectSummary(projectId)
                                                   };
            return View(viewModel);
        }

        /// <summary>
        ///     Method to return the list of documents submitted for the given Project
        /// </summary>
        /// <param name="projectId">
        ///     ProjectId for which document is to be retrieved.
        /// </param>
        /// <param name="page">
        ///     Current Page No.
        /// </param>
        /// <returns>
        ///     ViewModel containing list of documents for the given project.
        /// </returns>
        public ActionResult Documents(int projectId, int? page)
        {
            ProjectDocumentsViewModel viewModel = new ProjectDocumentsViewModel
                                                      {
                                                          Documents = _projectService.GetDocuments(projectId.ToString(CultureInfo.CurrentCulture), page.GetValueOrDefault(0).ToString(CultureInfo.CurrentCulture), _pageSize),
                                                          ProjectSummary = GetProjectSummary(projectId)
                                                      };
            return View(viewModel);
        }

        /// <summary>
        ///     Method to return the document for the given document id.
        /// </summary>
        /// <param name="documentId">
        ///     Unique id of a document
        /// </param>
        /// <returns>
        ///     FileContentResult instance to allow user to download the document.
        /// </returns>
        /// <remarks>
        ///     Document is cached for 86400 seconds. i.e 24 hours
        /// </remarks>
        [OutputCache(Duration = 86400, VaryByParam = "documentId")]
        public ActionResult Document(int documentId)
        {
            ProjectDocument document = _projectService.FindDocumentById(documentId.ToString(CultureInfo.CurrentCulture));
            return File(document.Contents.ToArray(), ((DocType)document.DocumentType).Description(),
                        string.Concat(document.Title, ".",
                                      Enum.Parse(typeof(DocType), document.DocumentType.ToString(CultureInfo.CurrentCulture)).ToString()));
        }

        /// <summary>
        ///     Method to return the photo for the given photo id.
        /// </summary>
        /// <param name="photoId">
        ///     Unique id of a photo
        /// </param>
        /// <returns>
        ///     FileContentResult instance to allow user to download the photo.
        /// </returns>
        /// <remarks>
        ///     Photo is cached for 86400 seconds i.e 24 hours.
        /// </remarks>
        [OutputCache(Duration = 86400, VaryByParam = "photoId")]
        public ActionResult Photo(int photoId)
        {
            ProjectPhoto photo = _projectService.FindPhotoById(photoId.ToString(CultureInfo.CurrentCulture));

            ActionResult actionResult = File(photo.Image.ToArray(), ((PhotoType)photo.PhotoType).ToString());
            return actionResult;
        }

        /// <summary>
        ///     GET method to add a new photo
        /// </summary>
        /// <param name="projectId">
        ///     ProjectId for which photo is to  be retrieved.
        /// </param>
        /// <returns>
        ///     GET ViewModel for add photo
        /// </returns>
        [Authorize]
        public ActionResult SubmitPhoto(int projectId)
        {
            SubmitPhotoViewModel viewModel = new SubmitPhotoViewModel
                                                 {
                                                     ProjectSummary = GetProjectSummary(projectId),
                                                     Photo = new ProjectPhoto { ProjectId = projectId }
                                                 };

            return View(viewModel);
        }

        /// <summary>
        ///     POST Method to Add photo
        /// </summary>
        /// <param name="photo">
        ///     Photo object to be added
        /// </param>
        /// <returns>
        ///     Model with new Photo
        /// </returns>
        [Authorize]
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult SubmitPhoto(ProjectPhoto photo)
        {
            HttpPostedFileBase uploadedfile = Request.Files[0];
            string fileName = uploadedfile.FileName;
            photo.FilePath = fileName;
            string type = Path.GetExtension(fileName).Replace(".", string.Empty).ToLower(CultureInfo.CurrentCulture);
            try
            {
                PhotoType photoType = (PhotoType)Enum.Parse(typeof(PhotoType), type.ToSentenceCasing());
                photo.PhotoType = (short)photoType;
            }
            catch (ArgumentException)
            {
                photo.PhotoType = (short)PhotoType.None;
            }
            if (!photo.IsValid)
            {
                ModelState.AddModelError(photo.GetRuleViolations());
            }
            else
            {
                byte[] imageData = new byte[uploadedfile.InputStream.Length];
                uploadedfile.InputStream.Read(imageData, 0, imageData.Length);
                photo.Image = new System.Data.Linq.Binary(imageData);

                photo.SubmittedBy = User.Identity.Name;
                photo.DateUploaded = DateTime.Now;

                _projectService.AddPhoto(photo);
                return RedirectToAction("Photos", "Projects", new { projectId = photo.ProjectId });
            }
            SubmitPhotoViewModel viewModel = new SubmitPhotoViewModel
            {
                ProjectSummary = GetProjectSummary(photo.ProjectId),
                Photo = photo
            };
            return View(viewModel);
        }

        /// <summary>
        ///     Method to return the list of volunteers for the given Project
        /// </summary>
        /// <param name="projectId">
        ///     ProjectId for which volunteers is to be retrieved.
        /// </param>
        /// <param name="page">
        ///     Current Page No.
        /// </param>
        /// <returns>
        ///     ViewModel containing list of volunteers for the given project.
        /// </returns>
        public ActionResult Volunteers(int projectId, int? page)
        {
            PaginatedCollection<Volunteer> volunteers = _projectService.GetVolunteers(projectId.ToString(CultureInfo.CurrentCulture), page.GetValueOrDefault(0).ToString(CultureInfo.CurrentCulture), _pageSize);
            var currentUser = from volunteer in volunteers
                              where volunteer.UserName.ToLower(CultureInfo.CurrentCulture) == User.Identity.Name.ToLower(CultureInfo.CurrentCulture)
                              select true;

            ProjectVolunteersViewModel model = new ProjectVolunteersViewModel
                                                   {
                                                       ProjectSummary = GetProjectSummary(projectId),
                                                       Volunteer = volunteers,
                                                       AlreadyVolunteered = currentUser.FirstOrDefault()
                                                   };
            return View(model);

        }

        /// <summary>
        ///     GET method to add a new volunteer
        /// </summary>
        /// <param name="projectId">
        ///     ProjectId for which volunteer is to be added.
        /// </param>
        /// <returns>
        ///     GET ViewModel to add new volunteer
        /// </returns>
        public ActionResult Volunteer(int projectId)
        {

            Project project = _projectService.FindById(projectId.ToString(CultureInfo.CurrentCulture));
            Volunteer volunteer = project.Volunteers.Where(v => v.UserName == User.Identity.Name).FirstOrDefault();

            ProjectSummary summary = GetProjectSummary(projectId);
            string text = Resources.Stimulus360.ConfirmationMessage_Volunteers;
            ProjectVolunteerViewModel model = new ProjectVolunteerViewModel
            {
                ProjectSummary = summary,
                Volunteer = volunteer,
                DisplayText = string.Format(CultureInfo.CurrentCulture, text, summary.ProjectTitle)
            };
            return View(model);
        }

        /// <summary>
        ///     POST Method to Add volunteer
        /// </summary>
        /// <param name="projectId">
        ///     ProjectId for which volunteer is to be added.
        /// </param>
        /// <param name="userName">
        ///     User who added himself as a volunteer.
        /// </param>
        /// <returns>
        ///     Model with new volunteer
        /// </returns>
        [Authorize]
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Volunteer(int projectId, string userName)
        {
            Volunteer volunteer = new Volunteer { UserName = userName , DateVolunteered = DateTime.Now };
            _projectService.AddVolunteer(volunteer, projectId);
            ProjectSummary summary = GetProjectSummary(projectId);
            ProjectVolunteerViewModel model = new ProjectVolunteerViewModel
            {
                ProjectSummary = summary,
                Volunteer = volunteer,
                DisplayText = string.Format(CultureInfo.CurrentCulture, Resources.Stimulus360.SuccessMessage_Volunteers, summary.ProjectTitle)
            };
            return View(model);
        }

        /// <summary>
        ///     Method to retrive the Project Managers image
        /// </summary>
        /// <param name="userName">
        ///     Name of the user whose image to be retrive.
        /// </param>
        /// <returns>
        ///     The File Result content to render on the screen.
        /// </returns>
        public ActionResult UserImage(string userName)
        {
            var filename = Server.MapPath("~/") + @"Content\images\projHeadImg.png";
            var image = _userProfileService.GetUserThumbnail(userName);
            if (image == null)
            {
                return File(filename, "image/png");
            }
            return File(image, "image/png");
        }

        /// <summary>
        ///     Method creates the "Stimulus360.Models.Data.CustomTypes.ProjectSummary" object from the "Stimulus360.Data.Project" object.
        /// </summary>
        /// <param name="projectId">
        ///     Project id whose details to be retrive.
        /// </param>
        /// <returns>
        ///     Project Summary instance.
        /// </returns>
        private ProjectSummary GetProjectSummary(int projectId)
        {
            Project project = _projectService.FindById(projectId.ToString(CultureInfo.CurrentCulture));
            return new ProjectSummary
                       {
                           ProjectId = projectId,
                           ProjectTitle = project.Title,
                           SubmittedDate = project.DateCreated,
                           Submitter = project.UserProfile.UserName,
                           AverageRating = project.AverageRating.GetValueOrDefault(0),
                           FavoritesCount = project.FavoritesCount.GetValueOrDefault(0),
                           UserRatingsCount = project.UserRatingsCount.GetValueOrDefault(0),
                           StatusString = project.StatusString,
                           VolunteersRequired = project.VolunteersRequired
                       };
        }
    }
}
